package jdos.gui;

import jdos.Dosbox;
import jdos.cpu.CPU;
import jdos.cpu.Callback;
import jdos.cpu.Paging;
import jdos.debug.Debug_gui;
import jdos.dos.Dos_devices;
import jdos.dos.Dos_programs;
import jdos.gui.MainBase.ShutdownException;
import jdos.hardware.Cmos;
import jdos.hardware.DMA;
import jdos.hardware.Hardware;
import jdos.hardware.IO;
import jdos.hardware.Joystick;
import jdos.hardware.Keyboard;
import jdos.hardware.MPU401;
import jdos.hardware.Memory;
import jdos.hardware.Mixer;
import jdos.hardware.PCSpeaker;
import jdos.hardware.Pic;
import jdos.hardware.SBlaster;
import jdos.hardware.VGA;
import jdos.hardware.VGA_draw;
import jdos.hardware.serialport.Serialports;
import jdos.ints.Bios;
import jdos.ints.Int10;
import jdos.ints.Mouse;
import jdos.misc.Cross;
import jdos.misc.Log;
import jdos.misc.setup.*;
import jdos.sdl.GUI;

import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

public class MainBase {
    static protected GUI gui = null;

    static public void showProgress(String msg, int percent) {
        gui.showProgress(msg, percent);
    }
    
    public static final class GFX_CallBackFunctions_t{
        static final public int GFX_CallBackReset=0;
        static final public int GFX_CallBackStop=1;
        static final public int GFX_CallBackRedraw=2;
    }

    public static interface GFX_CallBack_t {
        public void call(int function);
    }

    static private long startTime = System.currentTimeMillis();
    // emulate SDL_GetTicks -- Gets the number of milliseconds since SDL library initialization.
    static public long GetTicks() {
        return (System.currentTimeMillis()-startTime);
    }
    // emulate SDL_GetTicks -- SDL_Delay -- Waits a specified number of milliseconds before returning.
    static public void Delay(long ms) {
        try {Thread.sleep(ms);} catch (Exception e){}
    }

    static public /*Bitu*/int GFX_GetRGB(/*Bit8u*/int red,/*Bit8u*/int green,/*Bit8u*/int blue) {
		return ((blue << 0) | (green << 8) | (red << 16)) | (255 << 24);	
    }

    static /*Bit32s*/int internal_cycles=0;
    static /*Bits*/int internal_frameskip=0;
    static public void GFX_SetTitle(/*Bit32s*/int cycles,/*Bits*/int frameskip,boolean paused){
        StringBuffer title = new StringBuffer();
        if(cycles != -1) internal_cycles = cycles;
        if(frameskip != -1) internal_frameskip = frameskip;
        if(CPU.CPU_CycleAutoAdjust) {
            //sprintf(title,"DOSBox %s, Cpu speed: max %3d%% cycles, Frameskip %2d, Program: %8s",VERSION,internal_cycles,internal_frameskip,RunningProgram);
            title.append("DOSBox ");
            title.append(Config.VERSION);
            title.append(", CPU speed: max ");
            title.append(internal_cycles);
            title.append("% cycles, Frameskip ");
            title.append(internal_frameskip);
        } else {
            //sprintf(title,"DOSBox %s, Cpu speed: %8d cycles, Frameskip %2d, Program: %8s",VERSION,internal_cycles,internal_frameskip,RunningProgram);
            title.append("DOSBox ");
            title.append(Config.VERSION);
            title.append(", CPU speed: ");
            title.append(internal_cycles);
            title.append(" cycles, Frameskip ");
            title.append(internal_frameskip);
        }

        if(paused) title.append(" PAUSED");
        if (gui != null)
            gui.setTitle(title.toString());
    }

    static public class FocusChangeEvent {
        public FocusChangeEvent(boolean hasfocus) {
            this.hasfocus = hasfocus;
        }
        public boolean hasfocus;
    }
    static public class ShutdownException extends RuntimeException{}
    static public class KillException extends RuntimeException{}

    static public void addEvent(Object o) {
        if (!paused) {
            events.add(o);
        }
    }

    static public boolean mouse_locked = false;
    static public float mouse_sensitivity = 100.0f;
    static protected boolean mouse_autoenable = false;
    static protected boolean mouse_autolock = false;
    static protected boolean mouse_requestlock = false;

    static public void GFX_CaptureMouse() {
        mouse_locked=!mouse_locked;
        if (mouse_locked) {
            //SDL_WM_GrabInput(SDL_GRAB_ON);
            gui.showCursor(false);
            gui.captureMouse(true);
        } else {
            //SDL_WM_GrabInput(SDL_GRAB_OFF);
            gui.captureMouse(false);
            if (mouse_autoenable || !mouse_autolock) gui.showCursor(true);
        }
    }
    
    static final public Object pauseMutex = new Object();
    static protected void handle(FocusChangeEvent event) {
        if (event.hasfocus) {
            SetPriority(priority_focus);
        } else {
            if (mouse_locked) {
                GFX_CaptureMouse();
            }
            if (priority_nofocus == PRIORITY_LEVELS.PRIORITY_LEVEL_PAUSE) {
                GFX_SetTitle(-1,-1,true);
			    Keyboard.KEYBOARD_ClrBuffer();
                synchronized (pauseMutex) {
                    try {pauseMutex.wait();} catch (Exception e){}
                }
                GFX_SetTitle(-1,-1,false);
            } else {
                SetPriority(priority_nofocus);
            }
        }
    }

    final static public Object paintMutex = new Object();

    static public void Mouse_AutoLock(boolean enable) {
        mouse_autolock=enable;
        if (mouse_autoenable) mouse_requestlock=enable;
        else {
            gui.showCursor(!enable);
            mouse_requestlock=false;
        }
    }

    static void SetPriority(int level) {
        if (true) return;
    }
    private static Mapper.MAPPER_Handler KillSwitch = new  Mapper.MAPPER_Handler() {
        public void call(boolean pressed) {
            if (pressed) {
                throw new KillException();
            }
        }
    };

    private static Mapper.MAPPER_Handler CaptureMouse = new  Mapper.MAPPER_Handler() {
        public void call(boolean pressed) {
            if (pressed) {
                GFX_CaptureMouse();
            }
        }
    };

    public static Mapper.MAPPER_Handler SwitchFullScreen = new  Mapper.MAPPER_Handler() {
        public void call(boolean pressed) {
            if (pressed)
                gui.fullScreenToggle();
        }
    };

    protected static boolean paused = false;
    public static boolean keyboardPaused = false;
    private static Mapper.MAPPER_Handler PauseDOSBox = new  Mapper.MAPPER_Handler() {
        public void call(boolean pressed) {
            if (!pressed) {
                return;
            }
            GFX_SetTitle(-1,-1,true);
            synchronized (pauseMutex) {
                paused = true;
                keyboardPaused = true;
                try {pauseMutex.wait();} catch (Exception e){}
                paused = false;
                keyboardPaused = true;
            }
            GFX_SetTitle(-1,-1,false);
        }
    };

    static final class PRIORITY_LEVELS {
        static public final int PRIORITY_LEVEL_PAUSE=0;
        static public final int PRIORITY_LEVEL_LOWEST=1;
        static public final int PRIORITY_LEVEL_LOWER=2;
        static public final int PRIORITY_LEVEL_NORMAL=3;
        static public final int PRIORITY_LEVEL_HIGHER=4;
        static public final int PRIORITY_LEVEL_HIGHEST=5;
    }

    private final static int priority_focus = PRIORITY_LEVELS.PRIORITY_LEVEL_NORMAL;
    private final static int priority_nofocus = PRIORITY_LEVELS.PRIORITY_LEVEL_NORMAL;

    public static void GUI_StartUp() {

//            if      (focus.equals("lowest"))  { priority_focus = PRIORITY_LEVELS.PRIORITY_LEVEL_LOWEST;  }
//            else if (focus.equals("lower"))   { priority_focus = PRIORITY_LEVELS.PRIORITY_LEVEL_LOWER;   }
//            else if (focus.equals("normal"))  { priority_focus = PRIORITY_LEVELS.PRIORITY_LEVEL_NORMAL;  }
//            else if (focus.equals("higher"))  { priority_focus = PRIORITY_LEVELS.PRIORITY_LEVEL_HIGHER;  }
//            else if (focus.equals("highest")) { priority_focus = PRIORITY_LEVELS.PRIORITY_LEVEL_HIGHEST; }
//
//            if      (notfocus.equals("lowest"))  { priority_nofocus=PRIORITY_LEVELS.PRIORITY_LEVEL_LOWEST;  }
//            else if (notfocus.equals("lower"))   { priority_nofocus=PRIORITY_LEVELS.PRIORITY_LEVEL_LOWER;   }
//            else if (notfocus.equals("normal"))  { priority_nofocus=PRIORITY_LEVELS.PRIORITY_LEVEL_NORMAL;  }
//            else if (notfocus.equals("higher"))  { priority_nofocus=PRIORITY_LEVELS.PRIORITY_LEVEL_HIGHER;  }
//            else if (notfocus.equals("highest")) { priority_nofocus=PRIORITY_LEVELS.PRIORITY_LEVEL_HIGHEST; }
//            else if (notfocus.equals("pause"))   {
//                /* we only check for pause here, because it makes no sense
//                 * for DOSBox to be paused while it has focus
//                 */
//                priority_nofocus=PRIORITY_LEVELS.PRIORITY_LEVEL_PAUSE;
//                // :TODO: test this, it will probably crash
//            }
        SetPriority(priority_focus); //Assume focus on startup

        //Integer autolock = Dosbox.control.cmdline.FindInt("-autolock", true);
        //if (autolock != null) {
        //    mouse_autoenable = autolock.intValue()==1;
        //} else {
            mouse_autoenable = true;//section.Get_bool("autolock");
        //}
        if (!mouse_autoenable) gui.showCursor(false);
        mouse_autolock = false;
        mouse_sensitivity = 100;//section.Get_int("sensitivity");
        
    }

    static private void Config_Add_SDL() {
    }

    static void launchcaptures(String edit) {
        String file = null;
        //Section t = Dosbox.control.GetSection("dosbox");
        //if(t != null) 
        	file = "d:\fdsa.fdsa";
//        if(t == null || file.equals(Section.NO_SUCH_PROPERTY)) {
//            Log.exit("Config system messed up.\n");
//        }
        String path = Cross.CreatePlatformConfigDir();
        path += file;
        Cross.CreateDir(path);
        if(new File(path).isDirectory()) {
            Log.exit(path+" doesn't exists or isn't a directory.\n");
        }
    /*	if(edit.empty()) {
            printf("no editor specified.\n");
            exit(1);
        }*/

        try {
            Process p = Runtime.getRuntime().exec(new String[] {edit,path});
            if (p != null)
                System.exit(0);
        } catch (Exception e) {

        }
        //if you get here the launching failed!
        Log.exit("can't find filemanager "+edit+"\n");
    }

    static void eraseconfigfile() {
        if(new File("dosbox.conf").exists()) {
            show_warning("Warning: dosbox.conf exists in current working directory.\nThis will override the configuration file at runtime.\n");
        }
        String path = Cross.CreatePlatformConfigDir() + Cross.GetPlatformConfigName();
        new File(path).delete();
        System.exit(0);
    }

    static void show_warning(String message) {
        // :TODO:
        Log.log_msg(message);
    }

    protected static Vector events = new Vector();
    protected static long startupTime;

    static void main(GUI g, String[] args) {
        gui = g;
        while (true) {
            CPU.initialize();
            MainBase.GFX_SetTitle(-1, -1, false);

            
            
            
            
            
            
            
            
            
            String captures;
            if (false) launchcaptures(captures);

            //Second parse -conf entries
            String path;

            //Dosbox.control.ParseConfigFile("dosbox.conf");

            GUI_StartUp();

    		Dosbox.DOSBOX_RealInit();

    		if (Config.C_DEBUG) Debug_gui.LOG_StartUp();

    		IO.IO_Init();//done
    		Paging.PAGING_Init();//done
    		Memory.MEM_Init();//done
    		Callback.CALLBACK_Init();
    		Pic.PIC_Init();//done
    		jdos.hardware.Timer.TIMER_Init();//done
    		Cmos.CMOS_Init();//done
    		VGA.VGA_Init();

    		Render.RENDER_Init();

    		CPU.CPU_Init();//done

    		DMA.DMA_Init();//done
    		Keyboard.KEYBOARD_Init();

    		Mixer.MIXER_Init();

    		Midi.MIDI_Init();//done
    		MPU401.MPU401_Init();//done

    		SBlaster.SBLASTER_Init();//done

    		PCSpeaker.PCSPEAKER_Init();//done
    		    		
    		Bios.BIOS_Init();//done
    		Int10.INT10_Init();
    		Mouse.MOUSE_Init(); //Must be after int10 as it uses CurMode
    		Joystick.JOYSTICK_Init();

    		Serialports.SERIAL_Init();

            try {
                startupTime = System.currentTimeMillis();
                //Dosbox.control.StartUp();
                //Shell.SHELL_Init();

                Main.GFX_SetSize();
                
                Timer timer = new Timer();
                TimerTask timerTask = new TimerTask() {
                    @Override
                    public void run() {
                		VGA_draw.Draw();
                    }
                };
                timer.scheduleAtFixedRate(timerTask, 30, 30);

                MainFrameCCPAL.Init();
                
            } catch (Dos_programs.RebootException e) {
                System.out.println("Rebooting");
                //try {myconf.Destroy();} catch (Exception e1){}
                //continue;
                //Log.exit("Power Off");
                //                    throw new ShutdownException();
            } catch (ShutdownException e) {
                System.out.println("Normal Shutdown");
                //try {myconf.Destroy();} catch (Exception e1){}
            } catch (KillException e) {
                System.out.println("Normal Shutdown");
//                if (!Dosbox.applet)
//                    System.exit(1);
            } catch (Exception e) {
                e.printStackTrace();
                if (!Dosbox.applet)
                    System.exit(1);
            } finally {
                events.clear();
            }
            break;
        }
    }
}
